/*++

Copyright (c) Microsoft Corporation. All rights reserved.

Module Name:
    Hw_ccmp.c

Abstract:
    Hw layer software-based CCMP encryption and decryption 
    functions
    
Revision History:
      When        What
    ----------    ----------------------------------------------
    08-01-2005    Created

Notes:

--*/
#include "hw_pcomp.h"
#include "hw_main.h"

#pragma warning(disable:4200)  // nameless struct/union
#pragma warning(disable:4201)  // nameless struct/union
#pragma warning(disable:4214)  // bit field types other than int
#pragma warning(disable:4748)

            
#include <packon.h>
typedef struct CCMP_NONCE {
    UCHAR Priority: 4;
    UCHAR Rsvd: 4;
    DOT11_MAC_ADDRESS Address2;
    UCHAR PN[6];
} CCMP_NONCE, * PCCMP_NONCE;


typedef struct DOT11_AAD_ALL_FIELDS {
    DOT11_FRAME_CTRL        FrameControl;
    DOT11_MAC_ADDRESS       Address1;
    DOT11_MAC_ADDRESS       Address2;
    DOT11_MAC_ADDRESS       Address3;
    DOT11_SEQUENCE_CONTROL  SequenceControl;

    union {
        struct {
            DOT11_MAC_ADDRESS A4;
            DOT11_QOS_CONTROL A4QoSControl;
        };

        DOT11_QOS_CONTROL A3QoSControl;
    };
} DOT11_AAD_ALL_FIELDS, *PDOT11_AAD_ALL_FIELDS;
#include <packoff.h>

#define AES_128_NR  (10)
#define AES_192_NR  (12)
#define AES_256_NR  (14)
typedef enum AESKeySize {
    AES_128 = 16,
    AES_192 = 24,
    AES_256 = 32
} AESKeySize;

typedef struct AESKeyStruct {
    int nr;
    unsigned long w[(AES_256_NR + 1) * 4];
} AESKeyStruct;

#define CCMP_NONCE_SIZE         13
#define CCMP_BSIZE              16
#define CCMP_KEY_SIZE           16
#define CCMP_MIC_SIZE           8
#define CCMP_HEADER_SIZE        8

#include <packon.h>
typedef union CCMP_COUNTER {
    ULONGLONG dummy[2];
    struct {
        UCHAR flag;
        UCHAR nonce[CCMP_NONCE_SIZE];
        USHORT bno;
    };
} CCMP_COUNTER, * PCCMP_COUNTER;

typedef union CCMP_DATA {
    ULONGLONG val64[2];
    UCHAR val8[CCMP_BSIZE];
} CCMP_DATA, * PCCMP_DATA;
#include <packoff.h>

typedef struct CCMP128_STATE {
    CCMP_DATA MAC;  // Message Authentication Code
    CCMP_DATA S;
    CCMP_COUNTER cnr;
    AESKeyStruct *ktab;
    USHORT bno;
#if DBG
    USHORT len;
#endif
    USHORT cur_loc;
} CCMP128_STATE, *PCCMP128_STATE;


#pragma warning(default:4200)
#pragma warning(default:4201)
#pragma warning(default:4214)

#define AES_ENCRYPT(out_blk,in_blk,k)  aes_encrypt(k,in_blk,out_blk)

#define htons(x)    _byteswap_ushort((USHORT)(x))
#define ntohs(x)    _byteswap_ushort((USHORT)(x))
#define htonl(x)    _byteswap_ulong((ULONG)(x))
#define ntohl(x)    _byteswap_ulong((ULONG)(x))

static const unsigned char GF_table2[256] = {
    0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e,
    0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e,
    0x20, 0x22, 0x24, 0x26, 0x28, 0x2a, 0x2c, 0x2e,
    0x30, 0x32, 0x34, 0x36, 0x38, 0x3a, 0x3c, 0x3e,
    0x40, 0x42, 0x44, 0x46, 0x48, 0x4a, 0x4c, 0x4e,
    0x50, 0x52, 0x54, 0x56, 0x58, 0x5a, 0x5c, 0x5e,
    0x60, 0x62, 0x64, 0x66, 0x68, 0x6a, 0x6c, 0x6e,
    0x70, 0x72, 0x74, 0x76, 0x78, 0x7a, 0x7c, 0x7e,
    0x80, 0x82, 0x84, 0x86, 0x88, 0x8a, 0x8c, 0x8e,
    0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e,
    0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac, 0xae,
    0xb0, 0xb2, 0xb4, 0xb6, 0xb8, 0xba, 0xbc, 0xbe,
    0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce,
    0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc, 0xde,
    0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee,
    0xf0, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0xfe,
    0x1b, 0x19, 0x1f, 0x1d, 0x13, 0x11, 0x17, 0x15,
    0x0b, 0x09, 0x0f, 0x0d, 0x03, 0x01, 0x07, 0x05,
    0x3b, 0x39, 0x3f, 0x3d, 0x33, 0x31, 0x37, 0x35,
    0x2b, 0x29, 0x2f, 0x2d, 0x23, 0x21, 0x27, 0x25,
    0x5b, 0x59, 0x5f, 0x5d, 0x53, 0x51, 0x57, 0x55,
    0x4b, 0x49, 0x4f, 0x4d, 0x43, 0x41, 0x47, 0x45,
    0x7b, 0x79, 0x7f, 0x7d, 0x73, 0x71, 0x77, 0x75,
    0x6b, 0x69, 0x6f, 0x6d, 0x63, 0x61, 0x67, 0x65,
    0x9b, 0x99, 0x9f, 0x9d, 0x93, 0x91, 0x97, 0x95,
    0x8b, 0x89, 0x8f, 0x8d, 0x83, 0x81, 0x87, 0x85,
    0xbb, 0xb9, 0xbf, 0xbd, 0xb3, 0xb1, 0xb7, 0xb5,
    0xab, 0xa9, 0xaf, 0xad, 0xa3, 0xa1, 0xa7, 0xa5,
    0xdb, 0xd9, 0xdf, 0xdd, 0xd3, 0xd1, 0xd7, 0xd5,
    0xcb, 0xc9, 0xcf, 0xcd, 0xc3, 0xc1, 0xc7, 0xc5,
    0xfb, 0xf9, 0xff, 0xfd, 0xf3, 0xf1, 0xf7, 0xf5,
    0xeb, 0xe9, 0xef, 0xed, 0xe3, 0xe1, 0xe7, 0xe5
};

static const unsigned char GF_table3[256] = {
    0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09,
    0x18, 0x1b, 0x1e, 0x1d, 0x14, 0x17, 0x12, 0x11,
    0x30, 0x33, 0x36, 0x35, 0x3c, 0x3f, 0x3a, 0x39,
    0x28, 0x2b, 0x2e, 0x2d, 0x24, 0x27, 0x22, 0x21,
    0x60, 0x63, 0x66, 0x65, 0x6c, 0x6f, 0x6a, 0x69,
    0x78, 0x7b, 0x7e, 0x7d, 0x74, 0x77, 0x72, 0x71,
    0x50, 0x53, 0x56, 0x55, 0x5c, 0x5f, 0x5a, 0x59,
    0x48, 0x4b, 0x4e, 0x4d, 0x44, 0x47, 0x42, 0x41,
    0xc0, 0xc3, 0xc6, 0xc5, 0xcc, 0xcf, 0xca, 0xc9,
    0xd8, 0xdb, 0xde, 0xdd, 0xd4, 0xd7, 0xd2, 0xd1,
    0xf0, 0xf3, 0xf6, 0xf5, 0xfc, 0xff, 0xfa, 0xf9,
    0xe8, 0xeb, 0xee, 0xed, 0xe4, 0xe7, 0xe2, 0xe1,
    0xa0, 0xa3, 0xa6, 0xa5, 0xac, 0xaf, 0xaa, 0xa9,
    0xb8, 0xbb, 0xbe, 0xbd, 0xb4, 0xb7, 0xb2, 0xb1,
    0x90, 0x93, 0x96, 0x95, 0x9c, 0x9f, 0x9a, 0x99,
    0x88, 0x8b, 0x8e, 0x8d, 0x84, 0x87, 0x82, 0x81,
    0x9b, 0x98, 0x9d, 0x9e, 0x97, 0x94, 0x91, 0x92,
    0x83, 0x80, 0x85, 0x86, 0x8f, 0x8c, 0x89, 0x8a,
    0xab, 0xa8, 0xad, 0xae, 0xa7, 0xa4, 0xa1, 0xa2,
    0xb3, 0xb0, 0xb5, 0xb6, 0xbf, 0xbc, 0xb9, 0xba,
    0xfb, 0xf8, 0xfd, 0xfe, 0xf7, 0xf4, 0xf1, 0xf2,
    0xe3, 0xe0, 0xe5, 0xe6, 0xef, 0xec, 0xe9, 0xea,
    0xcb, 0xc8, 0xcd, 0xce, 0xc7, 0xc4, 0xc1, 0xc2,
    0xd3, 0xd0, 0xd5, 0xd6, 0xdf, 0xdc, 0xd9, 0xda,
    0x5b, 0x58, 0x5d, 0x5e, 0x57, 0x54, 0x51, 0x52,
    0x43, 0x40, 0x45, 0x46, 0x4f, 0x4c, 0x49, 0x4a,
    0x6b, 0x68, 0x6d, 0x6e, 0x67, 0x64, 0x61, 0x62,
    0x73, 0x70, 0x75, 0x76, 0x7f, 0x7c, 0x79, 0x7a,
    0x3b, 0x38, 0x3d, 0x3e, 0x37, 0x34, 0x31, 0x32,
    0x23, 0x20, 0x25, 0x26, 0x2f, 0x2c, 0x29, 0x2a,
    0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02,
    0x13, 0x10, 0x15, 0x16, 0x1f, 0x1c, 0x19, 0x1a
};

static const unsigned char S[256] = {
    0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
    0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
    0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
    0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
    0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
    0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
    0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
    0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
    0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
    0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
    0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
    0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
    0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
    0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
    0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
    0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
    0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
    0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
    0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
    0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
    0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
    0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
    0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
    0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
    0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
    0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
    0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
    0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
    0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
    0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
    0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
    0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};

static const unsigned char GF_power_table[16] = {
    0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40,
    0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a
};

static const unsigned char invS[256] = {
    0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38,
    0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
    0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87,
    0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
    0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d,
    0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
    0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2,
    0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
    0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16,
    0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
    0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda,
    0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
    0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a,
    0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
    0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02,
    0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
    0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea,
    0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
    0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85,
    0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
    0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89,
    0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
    0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20,
    0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
    0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31,
    0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
    0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d,
    0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
    0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0,
    0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
    0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26,
    0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};

static const unsigned char GF_table9[256] = {
    0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
    0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
    0x90, 0x99, 0x82, 0x8b, 0xb4, 0xbd, 0xa6, 0xaf,
    0xd8, 0xd1, 0xca, 0xc3, 0xfc, 0xf5, 0xee, 0xe7,
    0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
    0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
    0xab, 0xa2, 0xb9, 0xb0, 0x8f, 0x86, 0x9d, 0x94,
    0xe3, 0xea, 0xf1, 0xf8, 0xc7, 0xce, 0xd5, 0xdc,
    0x76, 0x7f, 0x64, 0x6d, 0x52, 0x5b, 0x40, 0x49,
    0x3e, 0x37, 0x2c, 0x25, 0x1a, 0x13, 0x08, 0x01,
    0xe6, 0xef, 0xf4, 0xfd, 0xc2, 0xcb, 0xd0, 0xd9,
    0xae, 0xa7, 0xbc, 0xb5, 0x8a, 0x83, 0x98, 0x91,
    0x4d, 0x44, 0x5f, 0x56, 0x69, 0x60, 0x7b, 0x72,
    0x05, 0x0c, 0x17, 0x1e, 0x21, 0x28, 0x33, 0x3a,
    0xdd, 0xd4, 0xcf, 0xc6, 0xf9, 0xf0, 0xeb, 0xe2,
    0x95, 0x9c, 0x87, 0x8e, 0xb1, 0xb8, 0xa3, 0xaa,
    0xec, 0xe5, 0xfe, 0xf7, 0xc8, 0xc1, 0xda, 0xd3,
    0xa4, 0xad, 0xb6, 0xbf, 0x80, 0x89, 0x92, 0x9b,
    0x7c, 0x75, 0x6e, 0x67, 0x58, 0x51, 0x4a, 0x43,
    0x34, 0x3d, 0x26, 0x2f, 0x10, 0x19, 0x02, 0x0b,
    0xd7, 0xde, 0xc5, 0xcc, 0xf3, 0xfa, 0xe1, 0xe8,
    0x9f, 0x96, 0x8d, 0x84, 0xbb, 0xb2, 0xa9, 0xa0,
    0x47, 0x4e, 0x55, 0x5c, 0x63, 0x6a, 0x71, 0x78,
    0x0f, 0x06, 0x1d, 0x14, 0x2b, 0x22, 0x39, 0x30,
    0x9a, 0x93, 0x88, 0x81, 0xbe, 0xb7, 0xac, 0xa5,
    0xd2, 0xdb, 0xc0, 0xc9, 0xf6, 0xff, 0xe4, 0xed,
    0x0a, 0x03, 0x18, 0x11, 0x2e, 0x27, 0x3c, 0x35,
    0x42, 0x4b, 0x50, 0x59, 0x66, 0x6f, 0x74, 0x7d,
    0xa1, 0xa8, 0xb3, 0xba, 0x85, 0x8c, 0x97, 0x9e,
    0xe9, 0xe0, 0xfb, 0xf2, 0xcd, 0xc4, 0xdf, 0xd6,
    0x31, 0x38, 0x23, 0x2a, 0x15, 0x1c, 0x07, 0x0e,
    0x79, 0x70, 0x6b, 0x62, 0x5d, 0x54, 0x4f, 0x46
};

static const unsigned char GF_tableb[256] = {
    0x00, 0x0b, 0x16, 0x1d, 0x2c, 0x27, 0x3a, 0x31,
    0x58, 0x53, 0x4e, 0x45, 0x74, 0x7f, 0x62, 0x69,
    0xb0, 0xbb, 0xa6, 0xad, 0x9c, 0x97, 0x8a, 0x81,
    0xe8, 0xe3, 0xfe, 0xf5, 0xc4, 0xcf, 0xd2, 0xd9,
    0x7b, 0x70, 0x6d, 0x66, 0x57, 0x5c, 0x41, 0x4a,
    0x23, 0x28, 0x35, 0x3e, 0x0f, 0x04, 0x19, 0x12,
    0xcb, 0xc0, 0xdd, 0xd6, 0xe7, 0xec, 0xf1, 0xfa,
    0x93, 0x98, 0x85, 0x8e, 0xbf, 0xb4, 0xa9, 0xa2,
    0xf6, 0xfd, 0xe0, 0xeb, 0xda, 0xd1, 0xcc, 0xc7,
    0xae, 0xa5, 0xb8, 0xb3, 0x82, 0x89, 0x94, 0x9f,
    0x46, 0x4d, 0x50, 0x5b, 0x6a, 0x61, 0x7c, 0x77,
    0x1e, 0x15, 0x08, 0x03, 0x32, 0x39, 0x24, 0x2f,
    0x8d, 0x86, 0x9b, 0x90, 0xa1, 0xaa, 0xb7, 0xbc,
    0xd5, 0xde, 0xc3, 0xc8, 0xf9, 0xf2, 0xef, 0xe4,
    0x3d, 0x36, 0x2b, 0x20, 0x11, 0x1a, 0x07, 0x0c,
    0x65, 0x6e, 0x73, 0x78, 0x49, 0x42, 0x5f, 0x54,
    0xf7, 0xfc, 0xe1, 0xea, 0xdb, 0xd0, 0xcd, 0xc6,
    0xaf, 0xa4, 0xb9, 0xb2, 0x83, 0x88, 0x95, 0x9e,
    0x47, 0x4c, 0x51, 0x5a, 0x6b, 0x60, 0x7d, 0x76,
    0x1f, 0x14, 0x09, 0x02, 0x33, 0x38, 0x25, 0x2e,
    0x8c, 0x87, 0x9a, 0x91, 0xa0, 0xab, 0xb6, 0xbd,
    0xd4, 0xdf, 0xc2, 0xc9, 0xf8, 0xf3, 0xee, 0xe5,
    0x3c, 0x37, 0x2a, 0x21, 0x10, 0x1b, 0x06, 0x0d,
    0x64, 0x6f, 0x72, 0x79, 0x48, 0x43, 0x5e, 0x55,
    0x01, 0x0a, 0x17, 0x1c, 0x2d, 0x26, 0x3b, 0x30,
    0x59, 0x52, 0x4f, 0x44, 0x75, 0x7e, 0x63, 0x68,
    0xb1, 0xba, 0xa7, 0xac, 0x9d, 0x96, 0x8b, 0x80,
    0xe9, 0xe2, 0xff, 0xf4, 0xc5, 0xce, 0xd3, 0xd8,
    0x7a, 0x71, 0x6c, 0x67, 0x56, 0x5d, 0x40, 0x4b,
    0x22, 0x29, 0x34, 0x3f, 0x0e, 0x05, 0x18, 0x13,
    0xca, 0xc1, 0xdc, 0xd7, 0xe6, 0xed, 0xf0, 0xfb,
    0x92, 0x99, 0x84, 0x8f, 0xbe, 0xb5, 0xa8, 0xa3
};

static const unsigned char GF_tabled[256] = {
    0x00, 0x0d, 0x1a, 0x17, 0x34, 0x39, 0x2e, 0x23,
    0x68, 0x65, 0x72, 0x7f, 0x5c, 0x51, 0x46, 0x4b,
    0xd0, 0xdd, 0xca, 0xc7, 0xe4, 0xe9, 0xfe, 0xf3,
    0xb8, 0xb5, 0xa2, 0xaf, 0x8c, 0x81, 0x96, 0x9b,
    0xbb, 0xb6, 0xa1, 0xac, 0x8f, 0x82, 0x95, 0x98,
    0xd3, 0xde, 0xc9, 0xc4, 0xe7, 0xea, 0xfd, 0xf0,
    0x6b, 0x66, 0x71, 0x7c, 0x5f, 0x52, 0x45, 0x48,
    0x03, 0x0e, 0x19, 0x14, 0x37, 0x3a, 0x2d, 0x20,
    0x6d, 0x60, 0x77, 0x7a, 0x59, 0x54, 0x43, 0x4e,
    0x05, 0x08, 0x1f, 0x12, 0x31, 0x3c, 0x2b, 0x26,
    0xbd, 0xb0, 0xa7, 0xaa, 0x89, 0x84, 0x93, 0x9e,
    0xd5, 0xd8, 0xcf, 0xc2, 0xe1, 0xec, 0xfb, 0xf6,
    0xd6, 0xdb, 0xcc, 0xc1, 0xe2, 0xef, 0xf8, 0xf5,
    0xbe, 0xb3, 0xa4, 0xa9, 0x8a, 0x87, 0x90, 0x9d,
    0x06, 0x0b, 0x1c, 0x11, 0x32, 0x3f, 0x28, 0x25,
    0x6e, 0x63, 0x74, 0x79, 0x5a, 0x57, 0x40, 0x4d,
    0xda, 0xd7, 0xc0, 0xcd, 0xee, 0xe3, 0xf4, 0xf9,
    0xb2, 0xbf, 0xa8, 0xa5, 0x86, 0x8b, 0x9c, 0x91,
    0x0a, 0x07, 0x10, 0x1d, 0x3e, 0x33, 0x24, 0x29,
    0x62, 0x6f, 0x78, 0x75, 0x56, 0x5b, 0x4c, 0x41,
    0x61, 0x6c, 0x7b, 0x76, 0x55, 0x58, 0x4f, 0x42,
    0x09, 0x04, 0x13, 0x1e, 0x3d, 0x30, 0x27, 0x2a,
    0xb1, 0xbc, 0xab, 0xa6, 0x85, 0x88, 0x9f, 0x92,
    0xd9, 0xd4, 0xc3, 0xce, 0xed, 0xe0, 0xf7, 0xfa,
    0xb7, 0xba, 0xad, 0xa0, 0x83, 0x8e, 0x99, 0x94,
    0xdf, 0xd2, 0xc5, 0xc8, 0xeb, 0xe6, 0xf1, 0xfc,
    0x67, 0x6a, 0x7d, 0x70, 0x53, 0x5e, 0x49, 0x44,
    0x0f, 0x02, 0x15, 0x18, 0x3b, 0x36, 0x21, 0x2c,
    0x0c, 0x01, 0x16, 0x1b, 0x38, 0x35, 0x22, 0x2f,
    0x64, 0x69, 0x7e, 0x73, 0x50, 0x5d, 0x4a, 0x47,
    0xdc, 0xd1, 0xc6, 0xcb, 0xe8, 0xe5, 0xf2, 0xff,
    0xb4, 0xb9, 0xae, 0xa3, 0x80, 0x8d, 0x9a, 0x97
};

static const unsigned char GF_tablee[256] = {
    0x00, 0x0e, 0x1c, 0x12, 0x38, 0x36, 0x24, 0x2a,
    0x70, 0x7e, 0x6c, 0x62, 0x48, 0x46, 0x54, 0x5a,
    0xe0, 0xee, 0xfc, 0xf2, 0xd8, 0xd6, 0xc4, 0xca,
    0x90, 0x9e, 0x8c, 0x82, 0xa8, 0xa6, 0xb4, 0xba,
    0xdb, 0xd5, 0xc7, 0xc9, 0xe3, 0xed, 0xff, 0xf1,
    0xab, 0xa5, 0xb7, 0xb9, 0x93, 0x9d, 0x8f, 0x81,
    0x3b, 0x35, 0x27, 0x29, 0x03, 0x0d, 0x1f, 0x11,
    0x4b, 0x45, 0x57, 0x59, 0x73, 0x7d, 0x6f, 0x61,
    0xad, 0xa3, 0xb1, 0xbf, 0x95, 0x9b, 0x89, 0x87,
    0xdd, 0xd3, 0xc1, 0xcf, 0xe5, 0xeb, 0xf9, 0xf7,
    0x4d, 0x43, 0x51, 0x5f, 0x75, 0x7b, 0x69, 0x67,
    0x3d, 0x33, 0x21, 0x2f, 0x05, 0x0b, 0x19, 0x17,
    0x76, 0x78, 0x6a, 0x64, 0x4e, 0x40, 0x52, 0x5c,
    0x06, 0x08, 0x1a, 0x14, 0x3e, 0x30, 0x22, 0x2c,
    0x96, 0x98, 0x8a, 0x84, 0xae, 0xa0, 0xb2, 0xbc,
    0xe6, 0xe8, 0xfa, 0xf4, 0xde, 0xd0, 0xc2, 0xcc,
    0x41, 0x4f, 0x5d, 0x53, 0x79, 0x77, 0x65, 0x6b,
    0x31, 0x3f, 0x2d, 0x23, 0x09, 0x07, 0x15, 0x1b,
    0xa1, 0xaf, 0xbd, 0xb3, 0x99, 0x97, 0x85, 0x8b,
    0xd1, 0xdf, 0xcd, 0xc3, 0xe9, 0xe7, 0xf5, 0xfb,
    0x9a, 0x94, 0x86, 0x88, 0xa2, 0xac, 0xbe, 0xb0,
    0xea, 0xe4, 0xf6, 0xf8, 0xd2, 0xdc, 0xce, 0xc0,
    0x7a, 0x74, 0x66, 0x68, 0x42, 0x4c, 0x5e, 0x50,
    0x0a, 0x04, 0x16, 0x18, 0x32, 0x3c, 0x2e, 0x20,
    0xec, 0xe2, 0xf0, 0xfe, 0xd4, 0xda, 0xc8, 0xc6,
    0x9c, 0x92, 0x80, 0x8e, 0xa4, 0xaa, 0xb8, 0xb6,
    0x0c, 0x02, 0x10, 0x1e, 0x34, 0x3a, 0x28, 0x26,
    0x7c, 0x72, 0x60, 0x6e, 0x44, 0x4a, 0x58, 0x56,
    0x37, 0x39, 0x2b, 0x25, 0x0f, 0x01, 0x13, 0x1d,
    0x47, 0x49, 0x5b, 0x55, 0x7f, 0x71, 0x63, 0x6d,
    0xd7, 0xd9, 0xcb, 0xc5, 0xef, 0xe1, 0xf3, 0xfd,
    0xa7, 0xa9, 0xbb, 0xb5, 0x9f, 0x91, 0x83, 0x8d
};

static void
substitute_bytes(__bcount(16) unsigned char state[16], __in_bcount(256) const char unsigned * sb)
{
    int i;
    for (i = 0; i < 16; i++) {
        state[i] = sb[state[i]];
    }
}

static void
shift_rows(unsigned char state[16])
{
    unsigned char t;

    // 0th row

    // 1st row
    t = state[1];
    state[1] = state[5];
    state[5] = state[9];
    state[9] = state[13];
    state[13] = t;

    // 2nd row
    t = state[2];
    state[2] = state[10];
    state[10] = t;
    t = state[6];
    state[6] = state[14];
    state[14] = t;

    // 3rd row
    t = state[3];
    state[3] = state[15];
    state[15] = state[11];
    state[11] = state[7];
    state[7] = t;
}

static unsigned long mix_col(unsigned long l)
{
    unsigned long tmp;

    // little endian
    tmp =
        GF_table2[l & 0xff] ^
        GF_table3[(l >> 8) & 0xff] ^
        ((l >> 16) & 0xff) ^
        ((l >> 24) & 0xff);

    tmp |=
        ((l & 0xff) ^
        GF_table2[(l >> 8) & 0xff] ^
        GF_table3[(l >> 16) & 0xff] ^
        ((l >> 24) & 0xff)) << 8;

    tmp |=
        ((l & 0xff) ^
        ((l >> 8) & 0xff) ^
        GF_table2[(l >> 16) & 0xff] ^
        GF_table3[(l >> 24) & 0xff]) << 16;

    tmp |=
        (GF_table3[l & 0xff] ^
        ((l >> 8) & 0xff) ^
        ((l >> 16) & 0xff) ^
        GF_table2[(l >> 24) & 0xff]) << 24;

    return tmp;
}

static void
mix_columns(unsigned long state[4])
{
    state[0] = mix_col(state[0]);
    state[1] = mix_col(state[1]);
    state[2] = mix_col(state[2]);
    state[3] = mix_col(state[3]);
}

static void
add_round_key(
    unsigned long state[4],
    unsigned long w[4]
    )
{
    state[0] ^= w[0];
    state[1] ^= w[1];
    state[2] ^= w[2];
    state[3] ^= w[3];
}

static void
inv_shift_rows(unsigned char state[16])
{
    unsigned char t;

    // 0th row

    // 1st row
    t = state[1];
    state[1] = state[13];
    state[13] = state[9];
    state[9] = state[5];
    state[5] = t;

    // 2nd row
    t = state[2];
    state[2] = state[10];
    state[10] = t;
    t = state[6];
    state[6] = state[14];
    state[14] = t;

    // 3rd row
    t = state[3];
    state[3] = state[7];
    state[7] = state[11];
    state[11] = state[15];
    state[15] = t;
}

static unsigned long inv_mix_col(unsigned long l)
{
    unsigned long tmp;

    // little endian
    tmp =
        GF_tablee[l & 0xff] ^
        GF_tableb[(l >> 8) & 0xff] ^
        GF_tabled[(l >> 16) & 0xff] ^
        GF_table9[(l >> 24) & 0xff];

    tmp |=
        (GF_table9[l & 0xff] ^
        GF_tablee[(l >> 8) & 0xff] ^
        GF_tableb[(l >> 16) & 0xff] ^
        GF_tabled[(l >> 24) & 0xff]) << 8;

    tmp |=
        (GF_tabled[l & 0xff] ^
        GF_table9[(l >> 8) & 0xff] ^
        GF_tablee[(l >> 16) & 0xff] ^
        GF_tableb[(l >> 24) & 0xff]) << 16;

    tmp |=
        (GF_tableb[l & 0xff] ^
        GF_tabled[(l >> 8) & 0xff] ^
        GF_table9[(l >> 16) & 0xff] ^
        GF_tablee[(l >> 24) & 0xff]) << 24;

    return tmp;
}

static void
inv_mix_columns(unsigned long state[4])
{
    state[0] = inv_mix_col(state[0]);
    state[1] = inv_mix_col(state[1]);
    state[2] = inv_mix_col(state[2]);
    state[3] = inv_mix_col(state[3]);
}

void
aes_encrypt(
    AESKeyStruct * AESKey,
    void * in,
    void * out
    )
{
    int round;

    memcpy(out, in, 16);

    add_round_key(out, AESKey->w);

    for (round = 1; round < AESKey->nr; round++) {
        substitute_bytes(out, S);
        shift_rows(out);
        mix_columns(out);
        add_round_key(out, AESKey->w + round * 4);
    }

    substitute_bytes(out, S);
    shift_rows(out);
    add_round_key(out, AESKey->w + AESKey->nr * 4);
}

void
aes_decrypt(
    AESKeyStruct * AESKey,
    void * in,
    void * out
    )
{
    int round;

    memcpy(out, in, 16);

    add_round_key(out, AESKey->w + AESKey->nr * 4);

    for (round = AESKey->nr - 1; round >= 1; round--) {
        substitute_bytes(out, invS);
        inv_shift_rows(out);
        add_round_key(out, AESKey->w + round * 4);
        inv_mix_columns(out);
    }

    substitute_bytes(out, invS);
    inv_shift_rows(out);
    add_round_key(out, AESKey->w);
}

void
aes_key(
    AESKeyStruct * AESKey,
    AESKeySize klen,
    unsigned char * k
    )
{
    int i;
    unsigned long temp;
    int nk = klen / 4;
    unsigned long * kw = (void*)k;

    switch (klen) {
    case AES_128:
        AESKey->nr = AES_128_NR;
        break;

    case AES_192:
        AESKey->nr = AES_192_NR;
        break;

    case AES_256:
        AESKey->nr = AES_256_NR;
        break;

    default:
        ASSERT(0);
        return;
    }

    for(i = 0; i < nk; i++) {
        AESKey->w[i] = kw[i];
    }

    for(; i < 4 * (AESKey->nr + 1); i++) {
        temp = AESKey->w[i - 1];
        if (0 == (i % nk)) {
            temp = (temp >> 8) | (temp << 24);
            temp = S[temp & 0xff] |
                   ((unsigned long)(S[(temp >> 8) & 0xff]) << 8) |
                   ((unsigned long)(S[(temp >> 16) & 0xff]) << 16) |
                   ((unsigned long)(S[(temp >> 24) & 0xff]) << 24);
            ASSERT(i / nk < sizeof(GF_power_table) / sizeof(GF_power_table[0]));
            temp ^= GF_power_table[i / nk];
        } else if (nk > 6 && (i % nk) == 4) {
            temp = S[temp & 0xff] |
                   ((unsigned long)(S[(temp >> 8) & 0xff]) << 8) |
                   ((unsigned long)(S[(temp >> 16) & 0xff]) << 16) |
                   ((unsigned long)(S[(temp >> 24) & 0xff]) << 24);
        }

        AESKey->w[i] = AESKey->w[i - nk] ^ temp;
    }
}

void
ccmp128_init(
    AESKeyStruct * ktab,
    PUCHAR nonce,
    USHORT aad_len,
    PUCHAR aad,
    USHORT len,
    PCCMP128_STATE pState
    )
{
    unsigned char block[CCMP_BSIZE];
    ULONGLONG * p;
    USHORT i;

    if (aad_len) {
        // L = 2, M = 8, AAD present
        block[0] = 0x59;
    } else {
        // L = 2, M = 8, AAD not present
        block[0] = 0x19;
    }

    memcpy(block + 1, nonce, CCMP_NONCE_SIZE);
    pState->cnr.flag = 1;
    memcpy(pState->cnr.nonce, nonce, CCMP_NONCE_SIZE);
    pState->bno = 0;
    pState->cur_loc = 0;
    pState->ktab = ktab;

#if DBG
    pState->len = len;
#endif
    *((USHORT*)(block + 14)) = htons(len);

    // prepare for authentication
    AES_ENCRYPT(&pState->MAC, block, pState->ktab);
    if (!aad_len) {
        return;
    }

    if (aad_len < 14) {
        *((USHORT *)block) = htons((USHORT)aad_len);
        memcpy(block + 2, aad, aad_len);
        memset(block + 2 + aad_len, 0, 14 - aad_len);
        aad += aad_len;
        aad_len = 0;
    } else if (aad_len < 65536 - 256) {
        *((USHORT *)block) = htons((USHORT)aad_len);
        memcpy(block + 2, aad, 14);
        aad += 14;
        aad_len -= 14;
    } else {
#pragma warning( suppress : 28193 )
        *((USHORT *)block) = htons(0xfffe);
#pragma warning( suppress : 28193 )
        *((ULONG *)(block + 2)) = htonl(aad_len);   // The return value is assigned & saved
        memcpy(block + 6, aad, 10);
        aad += 10;
        aad_len -= 10;
    }

    p = (ULONGLONG*)block;
    pState->MAC.val64[0] ^= p[0];
    pState->MAC.val64[1] ^= p[1];
    AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);

    for (i = 0; i < (int)(aad_len & (~(CCMP_BSIZE - 1))); i += CCMP_BSIZE) {
        p = (ULONGLONG *)(aad + i);
        pState->MAC.val64[0] ^= p[0];
        pState->MAC.val64[1] ^= p[1];
        AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
    }

    if (i < aad_len) {
        aad_len = aad_len - i;
        memcpy(block, aad + i, aad_len);
        memset(block + aad_len, 0, CCMP_BSIZE - aad_len);

        p = (ULONGLONG *)block;
        pState->MAC.val64[0] ^= p[0];
        pState->MAC.val64[1] ^= p[1];
        AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
    }
}

void
ccmp128_encrypt(
    PCCMP128_STATE pState,
    USHORT len,
    UCHAR * buf
    )
{
    ULONGLONG * p;
    USHORT i;
    USHORT tmp;

    if (!len) {
        return;
    }

    tmp = (-pState->cur_loc) & (CCMP_BSIZE - 1);
    if (tmp) {
        int block_end = (tmp <= len);

        if (!block_end) {
            tmp = len;
        }

        len = len - tmp;
        i = pState->cur_loc & (CCMP_BSIZE - 1);
        pState->cur_loc = pState->cur_loc + tmp;
        while(tmp) {
            pState->MAC.val8[i] ^= *buf;
            *buf ^= pState->S.val8[i];
            buf++;
            tmp--;
            i++;
        }

        if (block_end) {
            AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
        }
    }

    p = (ULONGLONG*)buf;
    for (i = 0; i < (len & (~(CCMP_BSIZE - 1))); i += CCMP_BSIZE) {
        pState->bno++;
        pState->cnr.bno = htons(pState->bno);
        AES_ENCRYPT(&pState->S, &pState->cnr, pState->ktab);

        pState->MAC.val64[0] ^= p[0];
        pState->MAC.val64[1] ^= p[1];
        p[0] ^= pState->S.val64[0];
        p[1] ^= pState->S.val64[1];

        AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
        p += 2;
    }

    pState->cur_loc = pState->cur_loc + i;
    buf = (UCHAR *)p;
    len = len - i;

    if (len) {
        pState->bno++;
        pState->cnr.bno = htons(pState->bno);
        AES_ENCRYPT(&pState->S, &pState->cnr, pState->ktab);

        pState->cur_loc = pState->cur_loc + len;
        for(i = 0; i < len; i++) {
            pState->MAC.val8[i] ^= *buf;
            *buf ^= pState->S.val8[i];
            buf++;
        }
    }
}

void
ccmp128_decrypt(
    PCCMP128_STATE pState,
    USHORT len,
    UCHAR * buf
    )
{
    ULONGLONG * p;
    USHORT i;
    USHORT tmp;

    if (!len) {
        return;
    }

    tmp = (-pState->cur_loc) & (CCMP_BSIZE - 1);
    if (tmp) {
        int block_end = (tmp <= len);

        if (!block_end) {
            tmp = len;
        }

        len = len - tmp;
        i = pState->cur_loc & (CCMP_BSIZE - 1);
        pState->cur_loc = pState->cur_loc + tmp;
        while(tmp) {
            *buf ^= pState->S.val8[i];
            pState->MAC.val8[i] ^= *buf;
            tmp--;
            buf++;
            i++;
        }

        if (block_end) {
            AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
        }
    }

    p = (ULONGLONG*)buf;
    for (i = 0; i < (len & (~(CCMP_BSIZE - 1))); i += CCMP_BSIZE) {
        pState->bno++;
        pState->cnr.bno = htons(pState->bno);
        AES_ENCRYPT(&pState->S, &pState->cnr, pState->ktab);

        p[0] ^= pState->S.val64[0];
        p[1] ^= pState->S.val64[1];
        pState->MAC.val64[0] ^= p[0];
        pState->MAC.val64[1] ^= p[1];

        AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
        p += 2;
    }

    pState->cur_loc = pState->cur_loc + i;
    buf = (UCHAR *)p;
    len = len - i;

    if (len) {
        pState->bno++;
        pState->cnr.bno = htons(pState->bno);
        AES_ENCRYPT(&pState->S, &pState->cnr, pState->ktab);

        pState->cur_loc = pState->cur_loc + len;
        for(i = 0; i < len; i++) {
            *buf ^= pState->S.val8[i];
            pState->MAC.val8[i] ^= *buf;
            buf++;
        }
    }

    ASSERT(pState->cur_loc <= pState->len);
    return;
}

void
ccmp128_finalize(
    PCCMP128_STATE pState,
    void * MIC
    )
{
    ULONGLONG m;
    if (pState->cur_loc & (CCMP_BSIZE - 1)) {
        AES_ENCRYPT(&pState->MAC, &pState->MAC, pState->ktab);
    }

    pState->cnr.bno = 0;
    AES_ENCRYPT(&pState->S, &pState->cnr, pState->ktab);
    m = pState->MAC.val64[0] ^ pState->S.val64[0];
    memcpy(MIC, &m, CCMP_MIC_SIZE);

    ASSERT(pState->cur_loc == pState->len);
}


NDIS_STATUS
HwDecryptCCMP(
    PNICKEY pNicKey,
    PUCHAR  data,
    ULONG   size
    )
{
    ULONG                       headerSize;
    PDOT11_DATA_LONG_HEADER     MacHdr;
    CCMP_NONCE                  nonce;
    DOT11_AAD_ALL_FIELDS        aad;
    USHORT                      aadLength;
    ULONGLONG                   MIC;
    AESKeyStruct                AESKeyTable;
    CCMP128_STATE               state;

    //
    // NOTE: this function does not handle data frame with QoS field.
    //

    ASSERT(((PDOT11_MAC_HEADER)data)->FrameControl.WEP);
    ASSERT(pNicKey->Valid);
    ASSERT(pNicKey->AlgoId == DOT11_CIPHER_ALGO_CCMP);

    //
    // Data must contain MAC header, 8-byte CCMP header, at least 1-byte of encrypted PDU, followed by
    // 8-byte MIC.
    //
    MacHdr = (PDOT11_DATA_LONG_HEADER)data;
    if (MacHdr->FrameControl.FromDS && MacHdr->FrameControl.ToDS)
    {
        headerSize = sizeof(DOT11_DATA_LONG_HEADER);
        aadLength = FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, A4QoSControl);
    }
    else
    {
        headerSize = sizeof(DOT11_DATA_SHORT_HEADER);
        aadLength = FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, A3QoSControl);
    }

    if (size < headerSize + CCMP_HEADER_SIZE + 1 + CCMP_MIC_SIZE) 
    {
        return NDIS_STATUS_FAILURE;
    }

    //
    // Validate CCMP header. Reserved field (3rd byte) must be 0.
    // In 4th byte of CCMP header, ExtIV must be 1 and rsvd must be 0
    //
    data += headerSize;
    size -= headerSize;
    if (data[2] != 0)
    {
        return NDIS_STATUS_FAILURE;
    }

    if ((data[3] & 0x3f) != 0x20)
    {
        return NDIS_STATUS_FAILURE;
    }

    //
    // Construct Nonce and AAD
    //
    nonce.Priority = 0;
    nonce.Rsvd = 0;
    nonce.PN[0] = data[7];
    nonce.PN[1] = data[6];
    nonce.PN[2] = data[5];
    nonce.PN[3] = data[4];
    nonce.PN[4] = data[1];
    nonce.PN[5] = data[0];
    NdisMoveMemory(nonce.Address2, MacHdr->Address2, sizeof(DOT11_MAC_ADDRESS));

    aad.FrameControl = MacHdr->FrameControl;
    NdisMoveMemory(aad.Address1, MacHdr->Address1, headerSize - FIELD_OFFSET(DOT11_MAC_HEADER, Address1));

    aad.FrameControl.Subtype = 0;
    aad.FrameControl.Retry = 0;
    aad.FrameControl.PwrMgt = 0;
    aad.FrameControl.MoreData = 0;
    aad.SequenceControl.SequenceNumber = 0;

    //
    // Get the AES128 key table.
    //
    aes_key(&AESKeyTable, pNicKey->KeyLength, pNicKey->KeyValue);

    //
    // Initialize CCMP decryptor.
    //
    data += CCMP_HEADER_SIZE;
    size -= CCMP_HEADER_SIZE + CCMP_MIC_SIZE;                  // excluding MIC as well
    ccmp128_init(&AESKeyTable,
                 (PVOID)&nonce,
                 aadLength,
                 (PVOID)&aad,
                 (USHORT)size,
                 &state);

    //
    // Decrypt the payload 
    //
    ccmp128_decrypt(&state, (USHORT)size, data);

    //
    // Verify MIC
    //
    ccmp128_finalize(&state, &MIC);

    return (MIC == *((ULONGLONG UNALIGNED *)(data + size))) ? 
           NDIS_STATUS_SUCCESS : STATUS_DECRYPTION_FAILED;
}

NDIS_STATUS
HwEncryptCCMP(
    PNICKEY pNicKey,
    PNET_BUFFER NetBuffer
    )
{
    ULONG                       headerSize;
    PDOT11_DATA_LONG_HEADER     MacHdr;
    CCMP_NONCE                  nonce;
    DOT11_AAD_ALL_FIELDS        aad;
    USHORT                      aadLength;
    AESKeyStruct                AESKeyTable;
    CCMP128_STATE               state;
    PCHAR                       data;
    ULONG                       size;
    PMDL                        mdl;
    ULONG                       offset;

    //
    // This function assumes the following:
    // 1. 802.11 header and the CCMP header are in the first MDL.
    // 2. Last MDL has exactly 8 bytes which will be used to store MIC.
    // 3. Only the first MDL is guaranteed to be mapped to system virtual space.
    // 4. The data frame does not contain QoS field.
    //

    ASSERT(pNicKey->Valid);
    ASSERT(pNicKey->AlgoId == DOT11_CIPHER_ALGO_CCMP);

    //
    // Find 802.11 data header.
    //
    mdl = NET_BUFFER_CURRENT_MDL(NetBuffer);
    MacHdr = Add2Ptr(mdl->MappedSystemVa, NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer));
    ASSERT(MacHdr->FrameControl.WEP);

    //
    // Find CCMP header.
    //
    if (MacHdr->FrameControl.FromDS && MacHdr->FrameControl.ToDS)
    {
        headerSize = sizeof(DOT11_DATA_LONG_HEADER);
        aadLength = FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, A4QoSControl);
    }
    else
    {
        headerSize = sizeof(DOT11_DATA_SHORT_HEADER);
        aadLength = FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, A3QoSControl);
    }

    data = Add2Ptr(MacHdr, headerSize);

    //
    // Check the data length. It has to be bigger than MAC header, 8 bytes CCMP header and 8 bytes MIC.
    //
    size = NET_BUFFER_DATA_LENGTH(NetBuffer);
    if (size <= headerSize + CCMP_HEADER_SIZE + CCMP_MIC_SIZE)
    {
        return NDIS_STATUS_FAILURE;
    }

    //
    // Construct Nonce and AAD
    //
    nonce.Priority = 0;
    nonce.Rsvd = 0;
    nonce.PN[0] = data[7];
    nonce.PN[1] = data[6];
    nonce.PN[2] = data[5];
    nonce.PN[3] = data[4];
    nonce.PN[4] = data[1];
    nonce.PN[5] = data[0];
    NdisMoveMemory(nonce.Address2, MacHdr->Address2, sizeof(DOT11_MAC_ADDRESS));

    aad.FrameControl = MacHdr->FrameControl;
    
    if ((headerSize - FIELD_OFFSET(DOT11_MAC_HEADER, Address1)) > 
        (sizeof(DOT11_AAD_ALL_FIELDS) - FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, Address1)))
    {
       ASSERT(FALSE);
       return NDIS_STATUS_FAILURE;
    }
    //
    // Copy all the addresses into the aad
    //
    NdisMoveMemory((char *)(&aad)+FIELD_OFFSET(DOT11_AAD_ALL_FIELDS, Address1), MacHdr->Address1, headerSize - FIELD_OFFSET(DOT11_MAC_HEADER, Address1));
    //NdisMoveMemory(&aad.Address1[0], MacHdr->Address1, headerSize - FIELD_OFFSET(DOT11_MAC_HEADER, Address1));

    aad.FrameControl.Subtype = 0;
    aad.FrameControl.Retry = 0;
    aad.FrameControl.PwrMgt = 0;
    aad.FrameControl.MoreData = 0;
    aad.SequenceControl.SequenceNumber = 0;

    //
    // Get the AES128 key table.
    //
    aes_key(&AESKeyTable, pNicKey->KeyLength, pNicKey->KeyValue);

    //
    // Initialize CCMP decryptor.
    //
    size -= headerSize + CCMP_HEADER_SIZE + CCMP_MIC_SIZE;
    ccmp128_init(&AESKeyTable,
                 (PVOID)&nonce,
                 aadLength,
                 (PVOID)&aad,
                 (USHORT)size,
                 &state);

    //
    // Find the start of actual data.
    //
    offset = NET_BUFFER_CURRENT_MDL_OFFSET(NetBuffer) + headerSize + 8;
    ASSERT(offset <= mdl->ByteCount);
    if (offset == mdl->ByteCount)
    {
        mdl = mdl->Next;
        ASSERT(mdl);
        if (!mdl)
        {
            return NDIS_STATUS_FAILURE;
        }
        
        offset = 0;
    }

    //
    // Decrypt the payload 
    //
    while (mdl->Next)
    {
        if (!NdisBufferVirtualAddressSafe(mdl, NormalPagePriority))
        {
            return NDIS_STATUS_RESOURCES;
        }
                            
        ccmp128_encrypt(&state, 
                        (USHORT)(mdl->ByteCount - offset), 
                        Add2Ptr(mdl->MappedSystemVa, offset));
        mdl = mdl->Next;
        offset = 0;
    }

    //
    // Calculate MIC
    //
    ASSERT(mdl->ByteCount == CCMP_MIC_SIZE);
    data = NdisBufferVirtualAddressSafe(mdl, NormalPagePriority);
    if (!data)
    {
        return NDIS_STATUS_RESOURCES;
    }
    
    ccmp128_finalize(&state, data);

    return NDIS_STATUS_SUCCESS;
}

